// _GraphControl.cpp
#include "stdafx.h"
#include "_GraphControl.h"
#include <gl/gl.h>
#include <gl/glu.h>
// Static handlecounter for markers, used to generate unique handles
_u32 _GraphControl::MarkerHandle = 0;

// Constructor
_GraphControl::_GraphControl()
    :
    m_SelectionMarkerLeft(m_Layout),
    m_SelectionMarkerRight(m_Layout),
    m_XAxis(m_Layout),
    m_YAxis(m_Layout),
    m_Border(m_Layout),
    m_GraphArea(m_Layout),
    m_HandleCounter(0),
    m_Title(m_Layout),
    m_BackgroundColor(0xFFFFFF)
{
    m_PropertyDialog.setGraphControlPtr(this);
    m_MaxX = m_MaxAllGraphsX = -DBL_MAX;
    m_MaxY = m_MaxAllGraphsY = -DBL_MAX;
    m_MinX = m_MinAllGraphsX = DBL_MAX;
    m_MinY = m_MinAllGraphsY = DBL_MAX;
    m_ZoomStartPoint = CPoint(-1, -1);
    m_ZoomEndPoint = CPoint(-1, -1);
    m_ZoomActive = false;
}

// Destructor
_GraphControl::~_GraphControl()
{
}

// Message map for window messages
BEGIN_MESSAGE_MAP(_GraphControl, CWnd)
    //{{AFX_MSG_MAP(_GraphControl)
    ON_WM_CREATE()
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
    ON_WM_MOUSEMOVE()
    ON_WM_MOUSEWHEEL()
    ON_WM_ERASEBKGND()
    ON_WM_LBUTTONUP()
    ON_WM_LBUTTONDOWN()
    ON_WM_KEYUP()
    ON_WM_LBUTTONDBLCLK()
    ON_WM_CONTEXTMENU()
    ON_WM_MENUSELECT()
END_MESSAGE_MAP()


////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Setting opengl pixel format for initialisation
//
// @ Parameter:     hdc - device context handle
//
// @ Return:        VOID
////////////////////////////////////////////////////////////////////////
int _GraphControl::setPixelFormat(HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),    // size of this pfd
        1,                                // version number
        PFD_DRAW_TO_WINDOW |              // support window
        PFD_SUPPORT_OPENGL |              // support OpenGL
        PFD_DOUBLEBUFFER,                 // double buffered
        PFD_TYPE_RGBA,                    // RGBA type
        24,                               // 24-bit color depth
        0, 0, 0, 0, 0, 0,                 // color bits ignored
        0,                                // no alpha buffer
        0,                                // shift bit ignored
        0,                                // no accumulation buffer
        0, 0, 0, 0,                       // accum bits ignored
        16,                               // 16-bit z-buffer
        0,                                // no stencil buffer
        0,                                // no auxiliary buffer
        PFD_MAIN_PLANE,                   // main layer
        0,                                // reserved
        0, 0, 0                           // layer masks ignored
    };
    int  iPixelFormat;

    // get the device context's best, available pixel format match
    if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
    {
        MessageBox(NULL, _T("ChoosePixelFormat Failed"));
        return EXIT_FAILURE;
    }

    // make that match the device context's current pixel format
    if(SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
    {
        MessageBox(NULL, _T("SetPixelFormat Failed"));
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

// Initialises the OpenGL context
void _GraphControl::initialiseOpenGl()
{
    // Initialise device context for drawing
    hdc = ::GetDC(m_hWnd);
    hglrc = wglCreateContext(hdc);
    wglMakeCurrent(hdc, hglrc);
    // Initialise OpenGl
    initOpenGL();
    // Initialise matrices
    setPixelFormat(::GetDC(m_hWnd));
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  OnCreate handler. Called on control creation.
//                  Initialises opengl-window.
//
// @ Parameter:     lpCreateStruct - LPCREATESTRUCT
//
// @ Return:        EXIT_SUCCESS or EXIT_FAILURE
////////////////////////////////////////////////////////////////////////
int _GraphControl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // Default OnCreate handling
    if(CWnd::OnCreate(lpCreateStruct) == -1)
    {
        return EXIT_FAILURE;
    }

    // Init opengl
    initialiseOpenGl();
    // Init members
    GetClientRect(m_ControlRect);
    m_Layout.m_ControlRect = m_ControlRect;
    // Create drawing context
    hglrc = wglCreateContext(hdc);
    return EXIT_SUCCESS;
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Calculates the layout of the graph control.
//                  Uses the different parts to calculate the complete
//                  layout.
//
// @ Parameter:     void
//
// @ Return:        void
////////////////////////////////////////////////////////////////////////
void _GraphControl::calculateLayout()
{
    m_YAxis.calculateLayout(m_MinY, m_MaxY,
                            m_XAxis.getHeight(),
                            m_Title.getSize()
                           );
    m_XAxis.calculateLayout(m_MinX, m_MaxX,
                            m_YAxis.getWidth()
                           );
    m_Layout.m_XAxisHeight = m_XAxis.getHeight();
    m_Layout.m_YAxisWidth  = m_YAxis.getWidth();
    m_Layout.m_TitleHeight = m_Title.getSize();
}

void _GraphControl::setAxisColor(COLORREF Color)
{
    m_Title.setColor(Color);
    m_YAxis.setColor(Color);
    m_XAxis.setColor(Color);
}
void _GraphControl::setBackgroundColor(COLORREF Color)
{
    m_BackgroundColor = Color;
}
////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Fills the background of the control window
//                  with m_BackgroundColor
// @ Parameter:     void
//
// @ Return:        void
////////////////////////////////////////////////////////////////////////
void _GraphControl::drawBackground()
{
    // Fill complete control window with background color
    glViewport(0, 0, m_ControlRect.Width(), m_ControlRect.Height());
    glLoadIdentity();
    gluOrtho2D(0, 1, 0, 1);
    glColor3i(GET_GLINT_R(m_BackgroundColor),
              GET_GLINT_G(m_BackgroundColor),
              GET_GLINT_B(m_BackgroundColor)
             );
    glRectd(0, 0, 1, 1);
}

void _GraphControl::drawSelectionMarkers()
{
    if(m_SelectionMarkerLeft.Set)
    {
        m_SelectionMarkerLeft.draw(m_MinX, m_MaxX,
                                   m_Layout.m_BorderSize,
                                   m_Layout.m_YAxisWidth,
                                   m_Layout.m_XAxisHeight,
                                   m_Layout.m_TitleHeight);
    }

    if(m_SelectionMarkerRight.Set)
    {
        m_SelectionMarkerRight.draw(m_MinX, m_MaxX,
                                    m_Layout.m_BorderSize,
                                    m_Layout.m_YAxisWidth,
                                    m_Layout.m_XAxisHeight,
                                    m_Title.getSize());
    }
}

//////////////////////////////////////////////////////////////////////////
//// @ Beschreibung:  OnPaint handler. Used to paint the control.
////
//// @ Parameter:     VOID
////
//// @ Return:        VOID
//////////////////////////////////////////////////////////////////////////
void _GraphControl::OnPaint()
{
    // From http://msdn.microsoft.com/de-de/library/a48eab8d(v=vs.90).aspx
    // It performs a CWnd::BeginPaint at construction time and CWnd::EndPaint at destruction time.
    CPaintDC dc(this);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Make this window the current OpenGL context
    wglMakeCurrent(hdc, hglrc);
    // Calculate layout
    calculateLayout();
    // Fill everything with white
    drawBackground();

    // Draw markers
    for(auto Iterator = m_XMarkers.begin(); Iterator != m_XMarkers.end(); Iterator++)
    {
        if(Iterator->second.m_Visible)
        {
            Iterator->second.draw(m_MinX, m_MaxX,
                                  m_Layout.m_BorderSize,
                                  m_Layout.m_YAxisWidth,
                                  m_Layout.m_XAxisHeight,
                                  m_Layout.m_TitleHeight);
        }
    }

    for(auto Iterator = m_YMarkers.begin(); Iterator != m_YMarkers.end(); Iterator++)
    {
        if(Iterator->second.m_Visible)
        {
            Iterator->second.draw(m_MinY, m_MaxY,
                                  m_Layout.m_BorderSize,
                                  m_Layout.m_YAxisWidth,
                                  m_Layout.m_XAxisHeight,
                                  m_Layout.m_TitleHeight);
        }
    }

    // Draw graphs
    m_GraphArea.draw(m_Border.getSize(),
                     m_YAxis.getWidth(),
                     m_XAxis.getHeight(),
                     m_Title.getSize(),
                     m_XAxis.getNiceMin(), m_XAxis.getNiceMax(),
                     m_YAxis.getNiceMin(), m_YAxis.getNiceMax(),
                     m_GraphData, m_CurrentCursorPosition);
    // Draw selection markers
    drawSelectionMarkers();
    // Axes
    m_YAxis.draw(m_MinY, m_MaxY, m_DrawGrid);
    m_XAxis.draw(m_MinX, m_MaxX, m_DrawGrid);
    // Draw border
    m_Title.draw(m_Border.getSize());
    m_Border.draw();

    // Draw zoom rect
    if(m_ZoomActive)
    {
        m_GraphArea.drawZoomRect(m_ZoomStartPoint, m_ZoomEndPoint);
    }

    // Swap buffers (Double buffering to reduce flickering)
    SwapBuffers(hdc);
}

_s32 _GraphControl::calculateDefaultBoundaries()
{
    m_MaxAllGraphsX = m_MaxAllGraphsY = -DBL_MAX;
    m_MinAllGraphsX = m_MinAllGraphsY = DBL_MAX;
    int GraphCount = 0;

    for(auto Iterator = m_GraphData.begin(); Iterator != m_GraphData.end(); Iterator++)
    {
        if(Iterator->second.m_Visible == true)
        {
            GraphCount++;

            if(m_MaxAllGraphsX < Iterator->second.m_MaxX)
            {
                m_MaxAllGraphsX = Iterator->second.m_MaxX;
            }

            if(m_MinAllGraphsX > Iterator->second.m_MinX)
            {
                m_MinAllGraphsX = Iterator->second.m_MinX;
            }

            if(m_MaxAllGraphsY < Iterator->second.m_MaxY)
            {
                m_MaxAllGraphsY = Iterator->second.m_MaxY;
            }

            if(m_MinAllGraphsY > Iterator->second.m_MinY)
            {
                m_MinAllGraphsY = Iterator->second.m_MinY;
            }
        }
    }

    if(GraphCount == 0)
    {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

COLORREF _GraphControl::getAxisColor()
{
    return m_XAxis.getColor();
}

COLORREF _GraphControl::getBorderColor()
{
    return m_Border.getColor();
}

_u32 _GraphControl::getBorderSize()
{
    return m_Border.getSize();
}
void _GraphControl::removeMarker(int Handle)
{
    if(m_YMarkers.count(Handle) != 0)
    {
        m_YMarkers.erase(Handle);
        return;
    }

    if(m_XMarkers.count(Handle) != 0)
    {
        m_XMarkers.erase(Handle);
        return;
    }
}

int _GraphControl::addXMarker(double X, COLORREF Color, CString Comment)
{
    // Adds new Marker on X Axis
    _XMarker Marker = _XMarker(m_Layout, X, Color, Comment);
    m_XMarkers.insert(pair<_u32, _XMarker>(Marker.m_Handle, Marker));
    RedrawWindow();
    return Marker.m_Handle;
}

int _GraphControl::addYMarker(double Y, COLORREF Color, CString Comment)
{
    // Adds new Marker on X Axis
    _YMarker Marker = _YMarker(m_Layout, Y, Color, Comment);
    m_YMarkers.insert(pair<_u32, _YMarker>(Marker.m_Handle, Marker));
    RedrawWindow();
    return Marker.m_Handle;
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Set border visibility and color
//
// @ Parameter:     Visible - boolean
//                  Color   - Color as MFC COLORREF (RGB)
//
// @ Return:        _FGHandle
////////////////////////////////////////////////////////////////////////
void _GraphControl::setBorder(_u32 Size, bool Visible, COLORREF Color)
{
    m_Border.setColor(Color);
    m_Border.setVisible(Visible);
    m_Border.setSize(Size);
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Adds a new Graph
//
// @ Parameter:
//
// @ Return:        _FGHandle
////////////////////////////////////////////////////////////////////////
_FGHandle _GraphControl::addGraph(TCHAR* Title)
{
    m_GraphData.insert(pair<int, _GraphData>(m_HandleCounter, _GraphData(Title)));
    m_GraphData[m_HandleCounter].m_Handle = m_HandleCounter;
    return m_HandleCounter++;
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Adds a point to a graph specified by a handle
//
// @ Parameter:     Handle - Handle
//                  X      - X coordinate
//                  Y      - Y coordinate
//
// @ Return:        EXIT_SUCCESS
//                  EXIT_FAILURE
////////////////////////////////////////////////////////////////////////
void _GraphControl::addPoint(_FGHandle Handle, double X, double Y, bool UpdateBoundaries)
{
    bool GraphVisible;
    getGraphVisible(Handle, &GraphVisible);

    if(UpdateBoundaries && GraphVisible)
    {
        if(X > m_MaxX)
        {
            m_MaxX = X;
        }

        if(Y > m_MaxY)
        {
            m_MaxY = Y;
        }

        if(X < m_MinX)
        {
            m_MinX = X;
        }

        if(Y < m_MinY)
        {
            m_MinY = Y;
        }

        if(X > m_MaxAllGraphsX)
        {
            m_MaxAllGraphsX = X;
        }

        if(Y > m_MaxAllGraphsY)
        {
            m_MaxAllGraphsY = Y;
        }

        if(X < m_MinAllGraphsX)
        {
            m_MinAllGraphsX = X;
        }

        if(Y < m_MinAllGraphsY)
        {
            m_MinAllGraphsY = Y;
        }
    }

    m_GraphData[Handle].addPoint(X, Y);
}

void _GraphControl::getLineColor(_FGHandle Handle, _u32* Color)
{
    *Color = m_GraphData[Handle].m_LineColor;
}

void _GraphControl::setLineColor(_FGHandle Handle, _u32 Color)
{
    m_GraphData[Handle].m_LineColor = Color;
}

void _GraphControl::getPointColor(_FGHandle Handle, _u32* Color)
{
    *Color = m_GraphData[Handle].m_PointColor;
}

void _GraphControl::setPointColor(_FGHandle Handle, _u32 Color)
{
    m_GraphData[Handle].m_PointColor = Color;
}

void _GraphControl::getGraphVisible(_FGHandle Handle, bool* Visible)
{
    *Visible = m_GraphData[Handle].m_Visible;
}

void _GraphControl::setGraphVisible(_FGHandle Handle, bool Visible)
{
    m_GraphData[Handle].m_Visible = Visible;
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Handler for mouse movement. Panning the graph and
//                  redrawing when left button clicked and mouse moved.
//
// @ Parameter:     nFlags - Flags
//                  point  - Current mouse pointer position
//
// @ Return:        BOOL
////////////////////////////////////////////////////////////////////////
void _GraphControl::OnMouseMove(UINT nFlags, CPoint point)
{
    bool Redraw = false;

    if(m_GraphArea.m_MouseTrackingEnabled)
    {
        m_CurrentCursorPosition = point;
        Redraw = true;
    }

    if(nFlags & MK_LBUTTON)
    {
        // Zoom
        if(GetKeyState(VK_SHIFT) & 0x8000 && m_ZoomStartPoint.x != -1)
        {
            m_ZoomEndPoint = point;
            Redraw = true;
        }
        // Panning
        else if(m_LastPoint.x != -1)
        {
            const double ChangeX = (double) m_LastPoint.x - point.x;
            const double ChangeY = (double) m_LastPoint.y - point.y;
            m_MinX += ChangeX / m_GraphArea.getWidth() * (m_XAxis.getNiceMax() - m_XAxis.getNiceMin());
            m_MaxX += ChangeX / m_GraphArea.getWidth() * (m_XAxis.getNiceMax() - m_XAxis.getNiceMin());
            m_MinY -= ChangeY / m_GraphArea.getHeight() * (m_YAxis.getNiceMax() - m_YAxis.getNiceMin());
            m_MaxY -= ChangeY / m_GraphArea.getHeight() * (m_YAxis.getNiceMax() - m_YAxis.getNiceMin());
            Redraw = true;
        }

        m_LastPoint = point;
    }
    else
    {
        m_LastPoint = CPoint(-1, -1);
    }

    if(Redraw)
    {
        RedrawWindow();
    }

    CWnd::OnMouseMove(nFlags, point);
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Handler for mousewheel movement.
//                  Zooming in and out.
//
// @ Parameter:     nFlags - Flags
//                  zDelta - Scroll delta for mousewheel
//                  pt     - Current mouse pointer position
//
// @ Return:        BOOL
////////////////////////////////////////////////////////////////////////
BOOL _GraphControl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
    // Zooming
    m_MinX -= (m_MaxX - m_MinX) * (-zDelta / 3000.0f);
    m_MaxX += (m_MaxX - m_MinX) * (-zDelta / 3000.0f);
    m_MinY -= (m_MaxY - m_MinY) * (-zDelta / 3000.0f);
    m_MaxY += (m_MaxY - m_MinY) * (-zDelta / 3000.0f);
    RedrawWindow();
    return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}

////////////////////////////////////////////////////////////////////////
// @ Beschreibung:  Overridden to avoid flickering
////////////////////////////////////////////////////////////////////////
BOOL _GraphControl::OnEraseBkgnd(CDC* pDC)
{
    pDC;
    return FALSE;
}

void _GraphControl::OnLButtonUp(UINT nFlags, CPoint point)
{
    // If shift was pressed (zooming)
    if(GetKeyState(VK_SHIFT) & 0x8000)
    {
        m_ZoomEndPoint = point;

        if(m_ZoomEndPoint != m_ZoomStartPoint)
        {
            // Sort the values by size
            double Temp;

            if(m_ZoomEndPoint.x <= m_ZoomStartPoint.x)
            {
                Temp = m_ZoomStartPoint.x;
                m_ZoomStartPoint.x = m_ZoomEndPoint.x;
                m_ZoomEndPoint.x = (_s32) Temp;
            }

            if(m_ZoomEndPoint.y >= m_ZoomStartPoint.y)
            {
                Temp = m_ZoomStartPoint.y;
                m_ZoomStartPoint.y = m_ZoomEndPoint.y;
                m_ZoomEndPoint.y = (_s32) Temp;
            }

            setMinMax(m_GraphArea.getXValue(m_ZoomStartPoint.x),
                      m_GraphArea.getXValue(m_ZoomEndPoint.x),
                      m_GraphArea.getYValue(m_ZoomStartPoint.y),
                      m_GraphArea.getYValue(m_ZoomEndPoint.y));
            m_ZoomActive = false;
            m_ZoomStartPoint = CPoint(-1, -1);
            m_ZoomEndPoint = CPoint(-1, -1);
            RedrawWindow();
        }
    }
    // Control was pressed
    else if(GetKeyState(VK_CONTROL) & 0x8000)
    {
        // Add selection marker
        if(!m_SelectionMarkerLeft.Set && !m_SelectionMarkerRight.Set)
        {
            // Set the first marker
            m_SelectionMarkerLeft.m_Value = m_GraphArea.getXValue(point.x);
            m_SelectionMarkerLeft.Set = true;
        }
        else if(m_SelectionMarkerLeft.Set && !m_SelectionMarkerRight.Set)
        {
            // One marker already set
            double SelectedValue = m_GraphArea.getXValue(point.x);

            if(m_SelectionMarkerLeft.m_Value > SelectedValue)
            {
                m_SelectionMarkerRight.m_Value = m_SelectionMarkerLeft.m_Value;
                m_SelectionMarkerLeft.m_Value = SelectedValue;
            }
            else
            {
                m_SelectionMarkerRight.m_Value = SelectedValue;
            }

            m_SelectionMarkerRight.Set = true;

            for(auto GraphIterator = m_GraphData.begin(); GraphIterator != m_GraphData.end(); GraphIterator++)
            {
                GraphIterator->second.setSelection(m_SelectionMarkerLeft.m_Value, m_SelectionMarkerRight.m_Value);
            }
        }
        else if(m_SelectionMarkerLeft.Set && m_SelectionMarkerRight.Set)
        {
            // Delete both markers
            m_SelectionMarkerLeft.Set  = false;
            m_SelectionMarkerRight.Set = false;

            for(auto GraphIterator = m_GraphData.begin(); GraphIterator != m_GraphData.end(); GraphIterator++)
            {
                GraphIterator->second.resetSelection();
            }
        }
    }

    // Redraw
    RedrawWindow();
    CWnd::OnLButtonUp(nFlags, point);
}

void _GraphControl::OnLButtonDown(UINT nFlags, CPoint point)
{
    SetFocus();

    if(GetKeyState(VK_SHIFT) & 0x8000)
    {
        // Shift pressed, start zooming
        m_ZoomActive = true;
        m_ZoomStartPoint = point;
    }

    CWnd::OnLButtonDown(nFlags, point);
}

void _GraphControl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if(nChar == VK_SHIFT)
    {
        // Shift key was released stop zooming
        m_ZoomActive = false;
        m_ZoomStartPoint = CPoint(-1, -1);
        RedrawWindow();
    }

    CWnd::OnKeyUp(nChar, nRepCnt, nFlags);
}

// Default zoom
void _GraphControl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
    if(m_YAxis.isInsideAxis(point))
    {
        // Click auf y achse
        NewMarkerDialog Dialog(m_GraphArea.getYValue(point.y));

        if(Dialog.DoModal() == IDOK)
        {
            _YMarker Marker = _YMarker(m_Layout, Dialog.m_MarkerValue, Dialog.m_MarkerColor, Dialog.m_MarkerCaption);
            m_YMarkers.insert(pair<_u32, _YMarker>(Marker.m_Handle, Marker));
            RedrawWindow();
        }
    }
    else if(m_XAxis.isInsideAxis(point))
    {
        // Click auf x achse
        NewMarkerDialog Dialog(m_GraphArea.getXValue(point.x));

        if(Dialog.DoModal() == IDOK)
        {
            _XMarker Marker = _XMarker(m_Layout, Dialog.m_MarkerValue, Dialog.m_MarkerColor, Dialog.m_MarkerCaption);
            m_XMarkers.insert(pair<_u32, _XMarker>(Marker.m_Handle, Marker));
            RedrawWindow();
        }
    }
    else
    {
        setDefaultZoom();
    }

    CWnd::OnLButtonDblClk(nFlags, point);
}

void _GraphControl::OnContextMenu(CWnd* pWnd, CPoint point)
{
    CRect WndRectInScreenCoordinates = m_ControlRect;
    ClientToScreen(&WndRectInScreenCoordinates);

    if(WndRectInScreenCoordinates.PtInRect(point))
    {
        CMenu SubMenu, MainMenu;
        MainMenu.CreatePopupMenu();
        MainMenu.AppendMenu(MF_STRING, 4004, _T("Copy selection to clipboard"));
        MainMenu.AppendMenu(MF_STRING, 4003, _T("Paste from clipboard (csv)"));
        MainMenu.AppendMenu(MF_STRING, 4005, _T("Screenshot to clipboard"));
        MainMenu.AppendMenu(MF_STRING, 4006, _T("Reset zoom"));
        MainMenu.AppendMenu(MF_STRING, 4007, _T("Properties"));
        MainMenu.TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this);
    }
    else
    {
        CWnd::OnContextMenu(pWnd, point);
    }
}

void _GraphControl::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
    CWnd::OnMenuSelect(nItemID, nFlags, hSysMenu);
}

BOOL _GraphControl::OnCommand(WPARAM wParam, LPARAM lParam)
{
    switch(wParam)
    {
        case 4003:
            {
                // Paste csv
                HANDLE clip;

                if(OpenClipboard())
                {
                    clip = GetClipboardData(CF_UNICODETEXT);
                    CloseClipboard();
                }

                CString ClipboardText((TCHAR*) clip);
                CString TokenizedText;
                int CurPosNewLine = 0;
                bool FirstLine = true;
                vector<CString> Names;
                vector<vector<double>> GraphData;
                TokenizedText = ClipboardText.Tokenize(_T("\r\n"), CurPosNewLine);

                while(TokenizedText != _T(""))
                {
                    CString TokenizedTextCSV = TokenizedText;
                    int CurPosSemicolon = 0;
                    TokenizedTextCSV = TokenizedTextCSV.Tokenize(_T(";"), CurPosSemicolon);
                    int i = 0;

                    while(TokenizedTextCSV != _T(""))
                    {
                        if(FirstLine)
                        {
                            Names.push_back(TokenizedTextCSV);
                            GraphData.push_back(vector<double>());
                        }
                        else
                        {
                            double Temp;
                            swscanf_s(TokenizedTextCSV, L"%lf", &Temp);
                            GraphData[i].push_back(Temp);
                        }

                        TokenizedTextCSV = TokenizedText.Tokenize(_T(";"), CurPosSemicolon);
                        i++;
                    }

                    FirstLine = false;
                    TokenizedText = ClipboardText.Tokenize(_T("\r\n"), CurPosNewLine);
                }

                if(Names.size() % 2 != 0)
                {
                    // Invalid graph data
                    return TRUE;
                }
                else
                {
                    vector<int> Handles;

                    for(_u32 i = 0; i < Names.size(); i += 2)
                    {
                        CString Temp = Names[i];
                        Temp.Delete(Temp.GetLength() - 1);
                        Handles.push_back(addGraph(Temp.GetBuffer()));
                    }

                    for(_u32 i = 0; i < GraphData.size(); i += 2)
                    {
                        for(_u32 j = 0; j < GraphData[i].size(); j++)
                        {
                            if(i + 1 < GraphData.size() && j < GraphData[i].size() && j < GraphData[i + 1].size())
                            {
                                addPoint(Handles[i], GraphData[i][j], GraphData[i + 1][j]);
                            }
                        }
                    }
                }

                break;
            }

        case 4004:
            {
                vector<_2DPoint>* pSelectionData = new vector<_2DPoint>[m_GraphData.size()];
                int GraphNum = 0;

                for(auto GraphIterator = m_GraphData.begin(); GraphIterator != m_GraphData.end(); GraphIterator++ , GraphNum++)
                {
                    if(GraphIterator->second.m_Visible == true && GraphIterator->second.isSelectionActive())
                    {
                        for(int i = GraphIterator->second.m_SelectionMinIndex; i <= GraphIterator->second.m_SelectionMaxIndex; i++)
                        {
                            pSelectionData[GraphNum].push_back(GraphIterator->second.m_Points[i]);
                        }
                    }
                }

                CString SelectionCsvString;

                for(auto GraphIterator = m_GraphData.begin(); GraphIterator != m_GraphData.end(); GraphIterator++)
                {
                    SelectionCsvString.Append(GraphIterator->second.m_Title);
                    SelectionCsvString.Append(L" X");
                    SelectionCsvString.Append(L";");
                    SelectionCsvString.Append(GraphIterator->second.m_Title);
                    SelectionCsvString.Append(L" Y");
                    SelectionCsvString.Append(L";");
                }

                SelectionCsvString.Append(L"\r\n");
                bool DataLeft = false;
                _u32 i = 0;

                do
                {
                    DataLeft = false;
                    int GraphNum = 0;

                    for(auto Iterator = m_GraphData.begin(); Iterator != m_GraphData.end(); Iterator++, GraphNum++)
                    {
                        if(i < pSelectionData[GraphNum].size())
                        {
                            CString X;
                            X.Format(L"%E", pSelectionData[GraphNum][i].m_X);
                            CString Y;
                            Y.Format(L"%E", pSelectionData[GraphNum][i].m_Y);
                            SelectionCsvString.Append(X);
                            SelectionCsvString.Append(L";");
                            SelectionCsvString.Append(Y);
                            SelectionCsvString.Append(L";");
                            DataLeft = true;
                        }
                        else
                        {
                            SelectionCsvString.Append(L";;");
                        }
                    }

                    SelectionCsvString.Append(L"\r\n");
                    i++;
                }
                while(DataLeft == true);

                if(OpenClipboard())
                {
                    EmptyClipboard();
                    size_t size = (SelectionCsvString.GetLength() + 1) * sizeof(TCHAR);
                    HGLOBAL hClipboardData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);

                    if(hClipboardData)
                    {
                        TCHAR* pchData = (TCHAR*) GlobalLock(hClipboardData);

                        if(pchData)
                        {
                            memcpy(pchData, (TCHAR*) SelectionCsvString.GetString(), size);
                            GlobalUnlock(hClipboardData);
                            SetClipboardData(CF_UNICODETEXT, hClipboardData);
                        }
                    }

                    CloseClipboard();
                }

                delete[] pSelectionData;
                break;
            }

        case 4005:
            {
                // Copy to clipboard
                // detach and destroy the old bitmap if any attached
                Sleep(500);
                CRect m_ControlRect2 = m_ControlRect;
                ClientToScreen(m_ControlRect2);
                // create a screen and a memory device context
                HDC hDCScreen = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
                HDC hDCMem = ::CreateCompatibleDC(hDCScreen);
                // create a compatible bitmap and select it in the memory DC
                HBITMAP hBitmap =
                    ::CreateCompatibleBitmap(hDCScreen, m_ControlRect2.Width(), m_ControlRect2.Height());
                HBITMAP hBmpOld = (HBITMAP)::SelectObject(hDCMem, hBitmap);
                // bit-blit from screen to memory device context
                // note: CAPTUREBLT flag is required to capture layered windows
                DWORD dwRop = SRCCOPY | CAPTUREBLT;
                ::BitBlt(hDCMem,  0,  0, m_ControlRect2.Width(), m_ControlRect2.Height(),
                         hDCScreen, m_ControlRect2.left, m_ControlRect2.top, dwRop);
                // attach bitmap handle to this object
                //Attach(hBitmap);
                OpenClipboard();
                ::EmptyClipboard();
                ::SetClipboardData(CF_BITMAP, hBitmap);
                CloseClipboard();
                // restore the memory DC and perform cleanup
                ::SelectObject(hDCMem, hBmpOld);
                ::DeleteDC(hDCMem);
                ::DeleteDC(hDCScreen);
                break;
            }

        case 4006:
            OnLButtonDblClk(NULL, NULL);
            break;

        case 4007:
            {
                m_PropertyDialog.DoModal();
                break;
            }

        default:
            break;
    }

    RedrawWindow();
    return CWnd::OnCommand(wParam, lParam);
}

void _GraphControl::setMinMax(double X_Min, double X_Max, double Y_Min, double Y_Max)
{
    if(X_Min > X_Max)
    {
        double temp = X_Min;
        X_Min = X_Max;
        X_Max = temp;
    }

    if(Y_Min > Y_Max)
    {
        double temp = Y_Min;
        Y_Min = Y_Max;
        Y_Max = temp;
    }

    m_MinX = X_Min;
    m_MaxX = X_Max;
    m_MinY = Y_Min;
    m_MaxY = Y_Max;
}

void _GraphControl::setXCaption(CString& Caption)
{
    m_XAxis.setTitle(Caption);
}

void _GraphControl::setYCaption(CString& Caption)
{
    m_YAxis.setTitle(Caption);
}

CString& _GraphControl::getXCaption()
{
    return m_XAxis.getTitle();
}

CString& _GraphControl::getYCaption()
{
    return m_YAxis.getTitle();
}

void _GraphControl::setMouseTrackingEnabled(bool Enable)
{
    m_GraphArea.enableMouseTracker(Enable);
}

bool _GraphControl::getMouseTrackingEnabled()
{
    return m_GraphArea.m_MouseTrackingEnabled;
}

void _GraphControl::getMinMax(double* X_Min, double* X_Max, double* Y_Min, double* Y_Max)
{
    *X_Min = m_MinX;
    *X_Max = m_MaxX;
    *Y_Min = m_MinY;
    *Y_Max = m_MaxY;
}

void _GraphControl::setDefaultZoom()
{
    if(calculateDefaultBoundaries() == EXIT_SUCCESS)
    {
        if(m_MinAllGraphsX == m_MaxAllGraphsX)
        {
            m_MinX = m_MinAllGraphsX - 0.1 * (m_MaxAllGraphsX);
            m_MaxX = m_MaxAllGraphsX + 0.1 * (m_MaxAllGraphsX);
        }
        else
        {
            m_MinX = m_MinAllGraphsX - 0.1 * (m_MaxAllGraphsX - m_MinAllGraphsX);
            m_MaxX = m_MaxAllGraphsX + 0.1 * (m_MaxAllGraphsX - m_MinAllGraphsX);
        }

        if(m_MinAllGraphsY == m_MaxAllGraphsY)
        {
            m_MinY = m_MinAllGraphsY - 0.1 * (m_MaxAllGraphsY);
            m_MaxY = m_MaxAllGraphsY + 0.1 * (m_MaxAllGraphsY);
        }
        else
        {
            m_MinY = m_MinAllGraphsY - 0.1 * (m_MaxAllGraphsY - m_MinAllGraphsY);
            m_MaxY = m_MaxAllGraphsY + 0.1 * (m_MaxAllGraphsY - m_MinAllGraphsY);
        }

        if(m_MinX == m_MaxX)
        {
            m_MaxX += 1;
            m_MinX -= 1;
        }

        if(m_MinY == m_MaxY)
        {
            m_MaxY += 1;
            m_MinY -= 1;
        }

        RedrawWindow();
        Invalidate();
    }
}

void _GraphControl::removeGraph(_FGHandle GraphHandle)
{
    auto Iterator = m_GraphData.begin();
    int i = 0;

    for(; Iterator != m_GraphData.end(); i++)
    {
        if(Iterator->first == GraphHandle || GraphHandle == -1)
        {
            auto IteratorCopy = Iterator;
            Iterator++;
            m_GraphData.erase(IteratorCopy);

            if(GraphHandle != -1 || m_GraphData.size() == 0)
            {
                break;
            }
        }
    }
}

CString& _GraphControl::getTitle()
{
    return m_Title.getTitle();
}

void _GraphControl::setTitle(CString& Title)
{
    m_Title.setTitle(Title);
}

void _GraphControl::setBorderColor(COLORREF Color)
{
    m_Border.setColor(Color);
}

void _GraphControl::setBorderSize(_u32 Size)
{
    m_Layout.m_BorderSize = Size;
    m_Border.setSize(Size);
}


map<_u32, _XMarker>& _GraphControl::getXMarkers()
{
    return m_XMarkers;
}

map<_u32, _YMarker>& _GraphControl::getYMarkers()
{
    return m_YMarkers;
}